# -*- coding: utf-8 -*-

import os
import hashlib
from application import settings
from django.db import models
from core.models import CoreModel, table_prefix
from django.utils.translation import gettext_lazy as _
from django.db.models import UniqueConstraint
from django.utils import timezone
from datetime import timedelta


class Job(CoreModel):
    """任务管理"""

    product = models.ForeignKey(
        to="pms.Product",
        related_name="job_set",
        on_delete=models.CASCADE,
        db_constraint=False,
        verbose_name="被测产品",
    )
    owner = models.ForeignKey(
        to="sys.User",
        related_name="job_owner_set",
        on_delete=models.DO_NOTHING,
        db_constraint=False,
        verbose_name="负责人",
    )
    testplan = models.ForeignKey(
        null=True,
        blank=True,
        to="tms.TestPlan",
        related_name="job_set",
        on_delete=models.DO_NOTHING,
        db_constraint=False,
        verbose_name="测试计划",
    )
    env = models.ForeignKey(
        to="oms.Env",
        related_name="job_set",
        on_delete=models.DO_NOTHING,
        db_constraint=False,
        verbose_name="执行环境",
    )
    tcs = models.ManyToManyField(
        to="tms.TestCase",
        related_name="job_set",
        db_constraint=False,
        verbose_name="关联用例",
    )
    nodes = models.ManyToManyField(
        to="oms.Node",
        related_name="job_set",
        db_constraint=False,
        verbose_name="执行节点",
    )
    name = models.CharField(
        max_length=128, null=True, blank=True, verbose_name="任务名称"
    )
    type = models.CharField(max_length=50, default="0", verbose_name="任务类型")
    policy = models.CharField(max_length=50, default="0", verbose_name="执行策略")
    level = models.CharField(max_length=50, default="0", verbose_name="操作级别")
    plugin = models.ForeignKey(
        null=True,
        blank=True,
        to="JobPlugin",
        related_name="job_set",
        on_delete=models.DO_NOTHING,
        db_constraint=False,
        verbose_name="执行插件",
    )
    percentage = models.IntegerField(default=0, verbose_name="任务进度")
    cron = models.CharField(
        max_length=256, null=True, blank=True, verbose_name="cron表达式"
    )
    executor = models.CharField(
        null=True, blank=True, max_length=256, verbose_name="执行人"
    )
    run_time = models.DateTimeField(null=True, blank=True, verbose_name="启动时间")
    finished = models.DateTimeField(null=True, blank=True, verbose_name="完成时间")
    detail = models.TextField(null=True, blank=True, verbose_name="详细日志")

    def __str__(self) -> str:
        return self.name

    class Meta:
        db_table = table_prefix + "tms_job"
        verbose_name = "执行任务管理"
        verbose_name_plural = verbose_name
        ordering = ("-create_time",)


class JobExecutionManager(models.Manager):
    def delete_old_job_execution(self, max_age: int):
        """
        Delete old job executions from the database.

        :param max_age: The maximum age (in seconds). Executions that are older
        than this will be deleted.
        """
        self.filter(run_time__lte=timezone.now() - timedelta(seconds=max_age)).delete()


class JobExecution(CoreModel):

    job = models.ForeignKey(
        to="tms.Job", on_delete=models.CASCADE, verbose_name="所属任务"
    )
    name = models.CharField(
        null=True, blank=True, max_length=64, verbose_name="执行名称"
    )

    run_time = models.DateTimeField(db_index=True, verbose_name="开始时间")

    finished = models.DateTimeField(
        db_index=True, default=None, null=True, verbose_name="结束时间"
    )

    detail = models.TextField(null=True, blank=True, verbose_name="详细日志")

    objects = JobExecutionManager()

    def __str__(self):
        return f"{self.id}: job '{self.job_id}' ({self.status})"

    class Meta:
        db_table = table_prefix + "tms_job_execution"
        verbose_name = "执行记录管理"
        ordering = ("-run_time",)
        constraints = [
            UniqueConstraint(fields=["job_id", "run_time"], name="unique_job_execution")
        ]


class JobLog(CoreModel):
    job = models.ForeignKey(
        null=True,
        blank=True,
        to="tms.Job",
        related_name="joblog_set",
        on_delete=models.CASCADE,
        db_constraint=False,
        verbose_name="任务日志",
    )
    job_execution = models.ForeignKey(
        null=True,
        blank=True,
        to="tms.JobExecution",
        related_name="joblog_set",
        on_delete=models.CASCADE,
        db_constraint=False,
        verbose_name="执行日志",
    )
    testcase = models.ForeignKey(
        to="tms.TestCase",
        related_name="joblog_set",
        on_delete=models.CASCADE,
        db_constraint=False,
        verbose_name="关联用例",
    )
    product = models.ForeignKey(
        to="pms.Product",
        related_name="joblog_set",
        on_delete=models.DO_NOTHING,
        db_constraint=False,
        verbose_name="所属产品",
    )
    module = models.ForeignKey(
        to="pms.Module",
        related_name="joblog_set",
        on_delete=models.DO_NOTHING,
        db_constraint=False,
        verbose_name="所属模块",
    )
    number = models.CharField(max_length=256, verbose_name="用例编号")
    name = models.CharField(
        max_length=256, null=True, blank=True, verbose_name="用例名称"
    )
    level = models.CharField(max_length=50, default="0", verbose_name="用例级别")
    type = models.CharField(max_length=50, default="0", verbose_name="用例类型")
    stage = models.CharField(max_length=50, default="0", verbose_name="适用阶段")
    isauto = models.CharField(max_length=50, default="Y", verbose_name="是否可自动化")
    precondition = models.TextField(null=True, blank=True, verbose_name="预置条件")
    steps = models.JSONField(default=dict, verbose_name="操作步骤")
    detail = models.TextField(null=True, blank=True, verbose_name="详细日志")
    run_time = models.DateTimeField(
        null=True, blank=True, db_index=True, verbose_name="开始时间"
    )
    finished = models.DateTimeField(
        null=True, blank=True, db_index=True, verbose_name="结束时间"
    )
    analysis = models.CharField(
        null=True, blank=True, max_length=50, verbose_name="分析结论"
    )
    executor = models.CharField(
        null=True, blank=True, max_length=256, verbose_name="执行人"
    )
    analyser = models.CharField(
        null=True, blank=True, max_length=256, verbose_name="分析人"
    )
    script = models.TextField(null=True, blank=True, verbose_name="用例脚本")

    def __str__(self) -> str:
        return "%s(%s)" % (self.job, self.job_execution)

    class Meta:
        db_table = table_prefix + "tms_joblog"
        verbose_name = "执行结果管理"
        verbose_name_plural = verbose_name
        ordering = ("create_time",)


def media_file_name(instance, filename):
    h = instance.md5sum
    basename, ext = os.path.splitext(filename)
    return os.path.join("job/plugins", h[0:1], h[1:2], h + ext.lower())


class JobPlugin(CoreModel):
    """任务插件"""

    name = models.CharField(null=True, blank=True, max_length=50, verbose_name="名称")
    file = models.FileField(upload_to=media_file_name)
    size = models.IntegerField(default=0, verbose_name="文件大小")
    md5sum = models.CharField(blank=True, max_length=36, verbose_name="文件md5")

    def __str__(self) -> str:
        return self.file.url

    def save(self, *args, **kwargs):
        if not self.md5sum:  # file is new
            md5 = hashlib.md5()
            for chunk in self.file.chunks():
                md5.update(chunk)
            self.md5sum = md5.hexdigest()
        super(JobPlugin, self).save(*args, **kwargs)

    class Meta:
        db_table = table_prefix + "tms_job_plugin"
        verbose_name = "任务插件"
        verbose_name_plural = verbose_name
        ordering = ("-create_time",)
